/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ill_be_bach;

import ill_be_bach.model.Chord;
import ill_be_bach.model.Beat;
import ill_be_bach.model.Accidental;
import ill_be_bach.model.Tempo;
import ill_be_bach.model.Note;
import ill_be_bach.model.Key;
import ill_be_bach.model.KeySignature;
import ill_be_bach.model.Song;
import java.io.File;
import java.util.ArrayList;
import org.jfugue.*;

/**
 *
 * @author Abhi
 */
public class SongPlayer {
    Player jFuguePlayer = new Player() ;

    public void play(final Song song) {
        Runnable task = new Runnable() {
            public void run() {
                if (jFuguePlayer.isPaused()) {
                    jFuguePlayer.resume();
                }
                else if(!jFuguePlayer.isPlaying()) {
                    jFuguePlayer = new Player() ;
                    jFuguePlayer.play(getJFugueSong(song));
                }
            }
        };
        new Thread(task).start();
    }

    public void pause() {
        Runnable task = new Runnable() {
            public void run() {
                if (jFuguePlayer.isPlaying())
                    jFuguePlayer.pause();
            }
        };
        new Thread(task).start();
    }

    public void stop() {
       Runnable task = new Runnable() {
            public void run() {
                if (jFuguePlayer.isPlaying())
                    jFuguePlayer.stop();
            }
        };
        new Thread(task).start();
    }

    public void play(Chord chord, KeySignature keySignature) {
        if (!jFuguePlayer.isPlaying()) {
            jFuguePlayer = new Player() ;
            jFuguePlayer.play(getJFugueKeySignature(keySignature) + " " + getJFugueChord(chord));
        }
    }

    public boolean saveAsMidi(Song song, File file) {
        try {
            jFuguePlayer.saveMidi(getJFugueSong(song), file);
            return true ;
        }
        catch (Exception ex) {
            return false ;
        }
    }

    public Song loadMidi(File file) {

        try {
            Pattern pattern  = jFuguePlayer.loadMidi(file);
            return getSong(pattern);
        }
        catch (Exception ex) {
            return null ;
        }
    }

    private Song getSong(Pattern pattern) {
        Song song = new Song();
        int voice = 0 ;
        int curPosition = 0 ;
        boolean addToChord = false ;
        ArrayList<Note> notes = new ArrayList<Note>();
        for (String token : pattern.getTokens()) {
            if (token.startsWith("T")) {
                song.setTempo(Tempo.get(new Integer(token.substring(1))));
            }
            else if(token.startsWith("V")) {
                voice = new Integer(token.substring(1));
            }
            else if (token.startsWith("@")) {
                int nextPosition = new Integer(token.substring(1));
                addToChord = nextPosition == curPosition && !song.getChords(voice).isEmpty();
                
                int restBeat = (nextPosition - curPosition) / 15;
                if (restBeat > 0) {
                    ArrayList<Chord> chords = song.getChords(voice);
                    if (!chords.isEmpty()) {
                        int lastChordBeat = chords.get(chords.size() - 1).beat.getEighthBeatsCount();
                        restBeat -= lastChordBeat ;
                    }
                    if (restBeat > 0) {
                        song.addChord(new Chord(new Note(Key.Rest, Accidental.None, 0), Beat.get(restBeat)), voice);
                    }
                }

                curPosition = nextPosition ;
            }
            else  {
                String key ;
                if (token.indexOf('#') != -1 || token.indexOf('b') != -1)
                    key = token.substring(0, 2);
                else
                    key = token.substring(0, 1);
                char beatChar = token.charAt(key.length() + 1);
                Beat beat = Beat.EIGHTH;
                if (beatChar == 't')
                    beat = Beat.EIGHTH;
                else if (beatChar == 's')
                    beat = Beat.QUARTER;
                else if (beatChar == 'i')
                    beat = Beat.HALF;
                else if (beatChar == 'q')
                    beat = Beat.WHOLE;
                if (!addToChord) {
                    if (notes.size() > 0) {
                        song.addChord(new Chord(notes, beat), voice);
                    }
                    notes = new ArrayList<Note>() ;
                }
                notes.add(new Note(key, token.charAt(key.length()) - 48));
            }
        }
        return song ;
    }

    private String getJFugueSong(Song song) {
        StringBuffer jFugueSong = new StringBuffer() ;
        if (song.getTempo() != null)
            jFugueSong.append("T").append(song.getTempo().getBeatsPerMinute()).append(" ");
        jFugueSong.append(getJFugueKeySignature(song.getKeySignature())).append(" ");
        for (int i = 0 ; i < Song.getMaxVoices() ; i++) {
            if (song.getChords(i).size() > 0)
                jFugueSong.append("V").append(i).append(" ");
            // TODO: uncomment and better implement playing from current position
            //Chord currentChord = song.getCurrentChord(i);
            //boolean play = currentChord == null ; // start playing from current chord
            for (Chord chord : song.getChords(i)) {
                //if (chord == currentChord)
                //    play = true ;
                //if (play)
                    jFugueSong.append(getJFugueChord(chord)).append(" ");
            }
        }
        return jFugueSong.toString() ;
    }

    private String getJFugueChord(Chord chord) {
        StringBuffer jFugueNote = new StringBuffer() ;
        for (int i = 0 ; i < chord.notes.size() ; i++) {
            Note note = chord.notes.get(i);
            Key key = note.key ;
            if (key == Key.C)
                jFugueNote.append("C");
            else if(key == Key.D)
                jFugueNote.append("D");
            else if (key == Key.E)
                jFugueNote.append("E");
            else if (key == Key.F)
                jFugueNote.append("F");
            else if (key == Key.G)
                jFugueNote.append("G");
            else if (key == Key.A)
                jFugueNote.append("A");
            else if (key == Key.B)
                jFugueNote.append("B");
            else if (key == Key.Rest)
                jFugueNote.append("R");

            if (note.accidental == Accidental.Sharp)
                jFugueNote.append("#");
            else if (note.accidental == Accidental.Flat)
                jFugueNote.append("b");
            else if (note.accidental == Accidental.Natural)
                jFugueNote.append("n");

            if (key != Key.Rest)
                jFugueNote.append(note.octave) ;

            Beat beat = chord.beat;
            if (beat == Beat.QUARTER)
                jFugueNote.append("q") ;
            else if (beat == Beat.EIGHTH)
                jFugueNote.append("i");
            else if (beat == Beat.HALF)
                jFugueNote.append("h");
            else if (beat == Beat.WHOLE)
                jFugueNote.append("w");

            if (i != chord.notes.size() - 1)
                jFugueNote.append("+");
        }
        return jFugueNote.toString();
    }

    private String getJFugueKeySignature(KeySignature keySignature) {
        String ret = "";
        if (keySignature == KeySignature.CMajor_AMinor)
            ret = "CMaj";
        else if (keySignature == KeySignature.GMajor_EMinor)
            ret = "Gmaj";
        else if (keySignature == KeySignature.DMajor_BMinor)
            ret = "DMaj";
        else if (keySignature == KeySignature.AMajor_FSharpMinor)
            ret = "AMaj";
        else if (keySignature == KeySignature.EMajor_CSharpMinor)
            ret = "EMaj";
        else if (keySignature == KeySignature.BMajor_GSharpMinor)
            ret = "BMaj";
        else if (keySignature == KeySignature.FSharpMajor_DSharpMinor)
            ret = "F#Maj";
        else if (keySignature == KeySignature.GFlatMajor_EFlatMinor)
            ret = "GbMaj";
        else if (keySignature == KeySignature.DFlatMajor_BFlatMinor)
            ret = "DbMaj";
        else if (keySignature == KeySignature.AFlatMajor_FMinor)
            ret = "AbMaj";
        else if (keySignature == KeySignature.EFlatMajor_CMinor)
            ret = "EbMaj";
        else if (keySignature == KeySignature.BFlatMajor_GMinor)
            ret = "BbMaj";
        else if (keySignature == KeySignature.FMajor_DMinor)
            ret = "FMaj";
        else
            return "";
        return "K" + ret ;
    }
}
